package com.greatwqs.wmall.product.provider.service.client;

import com.greatwqs.wmall.common.module.exception.AppException;
import com.greatwqs.wmall.common.module.exception.ErrorCode;
import com.greatwqs.wmall.common.module.util.JsonUtil;
import com.greatwqs.wmall.product.client.model.ProductResponse;
import com.greatwqs.wmall.product.client.model.ProductSubStockRequest;
import com.greatwqs.wmall.product.client.service.ProductClientService;
import com.greatwqs.wmall.product.provider.manager.ProductManager;
import com.greatwqs.wmall.product.provider.mapper.ProductMapper;
import com.greatwqs.wmall.product.provider.model.po.Product;

import lombok.extern.slf4j.Slf4j;

import org.apache.dubbo.config.annotation.DubboService;
import org.apache.seata.spring.annotation.GlobalTransactional;
import org.modelmapper.ModelMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Slf4j
@DubboService
public class ProductClientServiceImpl implements ProductClientService {

    @Autowired
    private ModelMapper modelMapper;

    @Autowired
    private ProductManager productManager;

    @Autowired
    private ProductMapper productMapper;

    @Override
    public ProductResponse getProductById(Long productId) {
        return modelMapper.map(productManager.getProductById(productId), ProductResponse.class);
    }

    @Override
    public List<ProductResponse> getProductListByIds(List<Long> productIds) {
        return productManager.getProductListByIds(productIds).stream()
                .map(product -> modelMapper.map(product, ProductResponse.class))
                .toList();
    }

    @Override
    public Map<Long, ProductResponse> getProductMapByIds(List<Long> productIds) {
        return this.getProductListByIds(productIds).stream()
                .collect(Collectors.toMap(ProductResponse::getProductId, product -> product));
    }

    @Override
    @GlobalTransactional(timeoutMills = 60000, name = "product-sub-stock")
    public void subProductStock(ProductSubStockRequest request) throws AppException {
        Product product = this.productManager.getProductById(request.getProductId());
        subProductStock(request, product);
    }

    @Override
    @GlobalTransactional(timeoutMills = 60000, name = "product-sub-stock")
    public void subProductStock(List<ProductSubStockRequest> requests) throws AppException {
        Collections.sort(requests);
        log.info("subProductStock, requests: {}", JsonUtil.toJson(requests));
        List<Long> productIds = requests.stream().map(ProductSubStockRequest::getProductId).toList();
        Map<Long, Product> productMap = this.productManager.getProductMapByIds(productIds);
        for (ProductSubStockRequest request : requests) {
            Product product = productMap.get(request.getProductId());
            subProductStock(request, product);
        }
    }

    private void subProductStock(ProductSubStockRequest request, Product product) {
        if (product == null) {
            log.warn("PRODUCT_NOT_FOUND, productId: {}", request.getProductId());
            throw new AppException(ErrorCode.PRODUCT_NOT_FOUND);
        }
        if (product.getStock() < request.getSubStock()) {
            log.warn("PRODUCT_STOCK_NOT_ENOUGH, productId: {}, request subStock: {}", request.getProductId(), request.getSubStock());
            throw new AppException(ErrorCode.PRODUCT_STOCK_NOT_ENOUGH);
        }

        // 乐观锁更新库存
        int effectRows = productMapper.updateSubStockByProductId(request.getProductId(), request.getSubStock(), product.getStock(), product.getUpdateVersion());
        if (effectRows != 1) {
            log.warn("PRODUCT_SUB_STOCK_FAILED, productId: {}, request subStock: {}", request.getProductId(), request.getSubStock());
            throw new AppException(ErrorCode.PRODUCT_SUB_STOCK_FAILED);
        }
    }
}
